/*
 * Copyright (C) 2010 Graham Allan
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.strath.cis.grallan.findbugs.adaptiveranking.gui;

import static java.awt.GridBagConstraints.BOTH;
import static java.awt.GridBagConstraints.HORIZONTAL;

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import edu.strath.cis.grallan.findbugs.adaptiveranking.AdaptiveRankingMode;
import edu.strath.cis.grallan.findbugs.adaptiveranking.SwingRankingMode;
import edu.strath.cis.grallan.findbugs.adaptiveranking.gui.IRankingModeHelpInfo.IQuestionAndAnswer;
import edu.strath.cis.grallan.findbugs.adaptiveranking.gui.IRankingViewController.DisplayState;
import edu.umd.cs.findbugs.annotations.NonNull;

/**
 * @author Graham Allan (grallan@cis.strath.ac.uk)
 */
public class AdaptiveRankingPanel extends JPanel {

	public static final String NAME = "AdaptiveRankingPanel";
	private final List<? extends SwingRankingMode> rankingModes;
	private AdaptiveRankingMode lastSelectedMode;

	private JPanel modeCardsPanel;

	public AdaptiveRankingPanel(List<? extends SwingRankingMode> rankingModes) {
		this.rankingModes = rankingModes;
		initialiseComponents();
	}

	private void initialiseComponents() {
		this.setName(NAME);
		this.setLayout(new GridBagLayout());

		GridBagConstraints con = new GridBagConstraints();
		con.insets = new Insets(6, 6, 6, 6);
		con.fill = HORIZONTAL;
		con.gridx = con.gridy = 0;
		con.weightx = 1;

		JComboBox modeSelectionBox = new JComboBox();

		modeCardsPanel = new JPanel(new CardLayout());

		SwingRankingMode initialMode = makeInitialMode();
		modeSelectionBox.addItem(new ModeMenuItem(initialMode));
		modeCardsPanel.add(initialMode.containerForMode(), initialMode.getName());
		lastSelectedMode = initialMode;

		for (SwingRankingMode mode : rankingModes) {
			modeSelectionBox.addItem(new ModeMenuItem(mode));

			String cardName = mode.getName();
			modeCardsPanel.add(mode.containerForMode(), cardName);
		}

		modeSelectionBox.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				JComboBox jcb = (JComboBox) e.getSource();
				ModeMenuItem selectedItem = (ModeMenuItem) jcb.getSelectedItem();
				selectedItem.getMode().select();

				CardLayout layout = (CardLayout) modeCardsPanel.getLayout();
				layout.show(modeCardsPanel, selectedItem.getMode().getName());

				lastSelectedMode = selectedItem.getMode();
			}
		});

		add(modeSelectionBox, con);

		con.insets = new Insets(6, 6, 6, 6);
		con.gridx = 0;
		con.gridy = 1;
		con.weightx = 1;
		con.weighty = 1;
		con.fill = BOTH;

		add(modeCardsPanel, con);
	}

	private SwingRankingMode makeInitialMode() {
		List<IRankingModeHelpInfo> modeHelpInfos = new ArrayList<IRankingModeHelpInfo>();
		for (SwingRankingMode rankingMode : this.rankingModes) {
			modeHelpInfos.add(rankingMode.helpInfo());
		}
		return new WelcomeRankingMode(modeHelpInfos);
	}

	private static class WelcomeRankingMode extends AbstractRankingMode implements SwingRankingMode {
		private JPanel container;
		private final List<IRankingModeHelpInfo> modeHelpInfos;

		public WelcomeRankingMode(List<IRankingModeHelpInfo> modeHelpInfos) {
			super("WelcomeMode", "Please select mode...", new WelcomeHelpInfo());
			this.modeHelpInfos = modeHelpInfos;
			this.container = new JPanel(new BorderLayout());

			JEditorPane helpPane = new JEditorPane();
			helpPane.setEditable(false);
			helpPane.setContentType("text/html; charset=UTF-8");

			// Localisation support will have to come later
			StringBuilder helpText = new StringBuilder();
			helpText.append("<h3>What is Findbugs Training?</h3>\n");
			helpText.append("<p>" + helpInfo().briefInformation() + "</p>");

			for (IQuestionAndAnswer questionAndAnswer : helpInfo().faqs()) {
				helpText.append("<h3>" + questionAndAnswer.question() + "</h3>");
				helpText.append("<p>" + questionAndAnswer.answer() + "</p>");
			}

			appendModeInfo(helpText);

			helpText.append("<p>Each mode will provide more information about how they work, to begin, select the "
					+ "mode that best described your usage, and follow the instructions.</p>");

			helpPane.setText(helpText.toString());
			helpPane.setCaretPosition(0);

			JScrollPane helpWrapper = new JScrollPane(helpPane);
			container.add(helpWrapper, BorderLayout.CENTER);

		}

		private void appendModeInfo(StringBuilder helpText) {

			if (modeHelpInfos.size() < 1) {
				return;
			}

			helpText.append("<p>The currently available modes are:</p>\n");
			helpText.append("<ul>\n");
			for (IRankingModeHelpInfo helpInfo : modeHelpInfos) {
				helpText.append("<li>");
				helpText.append("<b>" + helpInfo.modeName() + "</b>");
				helpText.append("<p>" + helpInfo.briefInformation() + "</b>");
				helpText.append("</li>");
			}
			helpText.append("</ul>\n");
		}

		public Container containerForMode() {
			return container;
		}

		public void select() {
			// no-op
		}

		private static class WelcomeHelpInfo extends AbstractHelpInfo {
			@Override
			protected String makeBriefInformationString() {
				return "Training is designed to let Findbugs learn what bug alerts are important to your "
						+ "project. Although the sorting and filtering mechanims available in Findbugs are flexible and "
						+ "powerful, they remain static, and discard the rich information about bug alerts provided over "
						+ "time. Training uses that information to order alerts based on how important they are to you, "
						+ "making the best use of your time.";
			}

			@Override
			protected List<IQuestionAndAnswer> makeFaqs() {
				QuestionAndAnswer whereToStart = new QuestionAndAnswer(
						"Where do I start?",
						"Training provides a flexible way to use the information provided by Findbugs "
								+ "analyses for different ranking schemes. Different modes may be designed for different uses, "
								+ "for example, some modes may be suitable for that first initial analysis on a large code base, "
								+ "while others may be more suitable for continuous use.</p>");
				return asList(whereToStart);
			}

			@Override
			protected String makeModeNameString() {
				return "Findbugs Training";
			}

		}

	}

	private static class ModeMenuItem {
		private final AdaptiveRankingMode mode;

		public ModeMenuItem(AdaptiveRankingMode mode) {
			this.mode = mode;
		}

		@Override
		public String toString() {
			return getMode().label();
		}

		public AdaptiveRankingMode getMode() {
			return mode;
		}
	}

	public DisplayState isDisplayedMode(@NonNull AdaptiveRankingMode rankingMode) {
		if (rankingMode == null || !rankingModes.contains(rankingMode)) {
			return DisplayState.NON_EXISTENT;
		} else if (!rankingMode.equals(lastSelectedMode)) {
			return DisplayState.NOT_DISPLAYED;
		} else {
			return DisplayState.DISPLAYED;
		}
	}

}